/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package models;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import play.db.jpa.Model;

/**
 * 
 * @author Ousama this class represent the Table for Entity in the ERD first the
 *         relations which are the foriegn keys Then the Model Methods
 */
@Entity
public class Entities extends Model {

    public String name;
    public boolean locked = false;
    public Date date = new Date();
    public int views = 0;
    public String description;
    @ManyToMany(cascade = CascadeType.PERSIST)
    public List<Organizer> managers = new ArrayList<Organizer>();
    @ManyToOne
    public Organization liesIn;
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Entity1")
    public List<IdeaDeveloper> blocked = new ArrayList<IdeaDeveloper>();
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Entity2")
    public List<IdeaDeveloper> followed = new ArrayList<IdeaDeveloper>();
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Entity3")
    public List<IdeaDeveloper> members = new ArrayList<IdeaDeveloper>();
    @OneToMany(mappedBy = "topicEnt", cascade = CascadeType.ALL)
    public List<Topic> topics = new ArrayList<Topic>();
    @OneToMany(mappedBy = "ideaEnt", cascade = CascadeType.ALL)
    public List<Idea> ideas = new ArrayList<Idea>();
    @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
    public List<Entities> childern = new ArrayList<Entities>();
    @OneToMany(mappedBy = "ent", cascade = CascadeType.ALL)
    public List<Request> requests = new ArrayList<Request>();
    @ManyToOne
    public Entities parent;
    public String type;
    @OneToMany(mappedBy = "entity", cascade = CascadeType.ALL)
    public List<Rate> rates = new ArrayList<Rate>();
    @ManyToMany
    public List<ActionItem> actionItemEnt = new ArrayList<ActionItem>();
    @ManyToMany(cascade = CascadeType.PERSIST)
    @JoinTable(name = "Entit10")
    public List<IdeaDeveloper> unfollowed = new ArrayList<IdeaDeveloper>();

    // public static Entities createEntity(IdeaDeveloper dev, String name,
    // Organization org){
    // Entities entity = new Entities();
    // entity.name = name;
    // entity.liesIn = org;
    // entity.save();
    // org.liesIn.add(entity);
    // org.save();
    // if(org.managers.contains(dev)){
    //
    // }
    //
    // }
    /**
     * @author Ousama
     *
     *         This method for adding a sub entity in an entity and checks first
     *         the scenarios which are I) The name is dublicated II)The adder is
     *         not allowed III)Everything is ok and it is added
     * @param entityName
     *            the name for the new entitiy
     * @param adderDeveloper
     *            the developer which asks to add that entitiy
     * @return Status String to tell the user the result of his request
     *
     */
    public String addEntity(String entityName, IdeaDeveloper adderDeveloper) {
        try {
            if (Entities.count("name = ? AND liesIn = ?", entityName,
                    this.liesIn) > 0) {
                return "The Name Is Not Avaliable";
            }
            Organizer lead = Organizer.find("ideaDeveloper = ?", adderDeveloper).first();

            OrganizationLead lead2 = OrganizationLead.find("ideaDeveloper = ?",
                    adderDeveloper).first();
            if (!this.managers.contains(lead)
                    && !this.liesIn.managers.contains(lead2)) {
                return "You Are Not Allowed";
            }
            Entities addedEntity = Entities.create(this);
            addedEntity.name = entityName;
            addedEntity.followed.add(adderDeveloper);
            addedEntity.save();

            if (lead != null) {
                addedEntity.managers.add(lead);
                addedEntity.members.add(lead.ideaDeveloper);
                addedEntity.save();
                lead.manages.add(addedEntity);
                lead.ideaDeveloper.joinedEnt.add(addedEntity);
                lead.save();
                adderDeveloper.followedEnt.add(addedEntity);
            }
            Log log = new Log("^ added sub ^ in ^");
            log.ids = adderDeveloper.id + "," + addedEntity.id + "," + this.id;
            log.types = "developer,entity,entity";
            log.save();
            adderDeveloper.incrementCounter(10);
            adderDeveloper.save();
            for (int i = 0; i < this.managers.size(); i++) {
                Notification.notify(" has created a sub-entity <a href='/application/entityview?entityId?entityId="
                        + addedEntity.id + "'>" + addedEntity.name + "</a>",
                        this.managers.get(i).ideaDeveloper, adderDeveloper,
                        new Date());
            }
//			for (int i = 0; i < this.liesIn.managers.size(); i++) {
//				Notification.notify(" has created a sub-entity ",
//						this.liesIn.managers.get(i).ideaDeveloper,
//						adderDeveloper, new Date());
//			}
            for (int i = 0; i < this.followed.size(); i++) {
                Notification.notify(" has created a sub-entity <a href='/application/entityview?entityId?entityId="
                        + addedEntity.id + "'>" + addedEntity.name + "</a>",
                        this.followed.get(i), adderDeveloper, new Date());
            }
            return "Done Adding";
        } catch (Exception exp) {
            return "You Are Not Allowed";
        }
    }

    /**
     * @author Ousama
     * @param org
     *            the organization where i will add a new entity in
     * @return the entitiy i just saved
     */
    public static Entities create(Organization org) {
        Entities entity = new Entities();
        entity.liesIn = org;
        entity.save();
        org.liesIn.add(entity);
        org.save();
        entity.type = "org";
        entity.parent = null;
        entity.date = new Date();
        entity.views = 0;
        entity.save();
        return entity;
    }

    /**
     * @author Ousama
     * @param ent
     *            the entity where i will add a new entity in
     * @return the entitiy i just saved
     */
    public static Entities create(Entities ent) {
        Entities entity = new Entities();
        entity.save();
        entity.type = "sub";
        entity.parent = ent;
        entity.date = new Date();
        entity.views = 0;
        entity.liesIn = ent.liesIn;
        entity.save();
        ent.childern.add(entity);
        ent.save();
        ent.liesIn.liesIn.add(entity);
        ent.liesIn.save();
        ent.date = new Date();
        return entity;
    }

    /**
     * @author OUSAMA This method for deleting the Entitity or the sub entity
     */
    public void delete2() {
        // delete it from the requests
        for (int i = 0; i < requests.size(); i++) {
            requests.get(i).delete2();
        }
        // deleting the managed from the manages IdeaDevelopers
        for (int i = 0; i < managers.size(); i++) {
            managers.get(i).manages.remove(this);
            managers.get(i).save();
        }
        // deleting from the followers
        for (int i = 0; i < followed.size(); i++) {
            followed.get(i).followedEnt.remove(this);
            followed.get(i).save();
        }
        for (int i = 0; i < unfollowed.size(); i++) {
            unfollowed.get(i).unfollowedEnt.remove(this);
            unfollowed.get(i).save();
        }
        // deleting from the blocked
        for (int i = 0; i < blocked.size(); i++) {
            blocked.get(i).blockedEnt.remove(this);
            blocked.get(i).save();
        }

        // deleting from the members
        for (int i = 0; i < members.size(); i++) {
            members.get(i).joinedEnt.remove(this);
            members.get(i).save();
        }
        // deleteing the sub entities in a recursive iterative way
        for (int i = 0; i < childern.size(); i++) {
            Entities sub = childern.remove(i);
            sub.delete2();
        }
        // deleting the inner topics
        while (!topics.isEmpty()) {
            topics.get(0).delete2();
        }
        // delete the contained ideas
        while (!ideas.isEmpty()) {
            ideas.get(0).delete2();
        }
        while (!rates.isEmpty()) {
            rates.get(0).delete2();
        }
        liesIn.liesIn.remove(this);
        liesIn.save();


        Log log = new Log("^ was deleted.");
        log.ids = this.id + "";
        log.types = "entity";
        log.save();
        this.delete();
    }

    /**
     * takes idea developer and entity and checks if the idea developer is
     * already following the given entity and is not blocked from the entity or
     * organization
     *
     * @author nataly.wadie
     * @param follower
     *            idea developer that needs checking if in the entity
     * @param entity
     *            entity that needs to be checked for the follower
     * @return returns strings with the result of every condition checked
     */
    public static boolean followCheck(IdeaDeveloper follower, Entities entity) {
        return entity.followed.contains(follower);
    }

    /**
     * takes idea developer and entity and checks if the user is already joined
     * in the organization that includes the entity and if he's not following
     * the entity then he will be added to the list of followers and the entity
     * will be added to the list of entities followed by the user
     *
     * @author nataly.wadie
     * @param follower
     *            the idea developer that wants to follow the entity
     * @param entity
     *            the entity that the idea developer wants to follow
     */
    public static void followEntity(IdeaDeveloper follower, Entities entity) {
        if (!followCheck(follower, entity)) {
            entity.followed.add(follower);
            entity.save();
            follower.followedEnt.add(entity);
            follower.save();
            if (entity.unfollowed.contains(follower)) {
                entity.unfollowed.remove(follower);
                entity.save();
                follower.unfollowedEnt.remove(entity);
                follower.save();
            }
            Log log = new Log("^ followed ^");
            log.ids = follower.id + "," + entity.id;
            log.types = "developer,entity";
            log.save();
            for (int i = 0; i < entity.managers.size(); i++) {
                IdeaDeveloper ideadeveloper = entity.managers.get(i).ideaDeveloper;
                Notification.notify(" has followed the entity <a href='/application/entityview?entityId="
                        + entity.id + "'>" + entity.name + "</a>",
                        ideadeveloper, follower, new Date());
            }
        }
    }

    /**
     * takes an idea developer and an entity and checks if the user is already
     * following this entity then he'll be removed from the followers of this
     * entity
     *
     * @author nataly.wadie
     * @param unfollower
     *            the idea developer that doesn't want to receive anymore
     *            notifications from the entity
     * @param entity
     *            the entity that will be unfollowed by the idea developer
     */
    public static void unfollowEntity(IdeaDeveloper unfollower, Entities entity) {
        if (followCheck(unfollower, entity)) {
            entity.followed.remove(unfollower);
            entity.save();
            unfollower.followedEnt.remove(entity);
            unfollower.save();
            unfollower.unfollowedEnt.add(entity);
            unfollower.save();
            entity.unfollowed.add(unfollower);
            entity.save();
            Log log = new Log("^ un-followed ^");
            log.ids = unfollower.id + "," + entity.id;
            log.types = "developer,entity";
            log.save();
            for (int i = 0; i < entity.managers.size(); i++) {
                IdeaDeveloper ideadeveloper = entity.managers.get(i).ideaDeveloper;
                Notification.notify(" has un-followed the entity <a href='/application/entityview?entityId="
                        + entity.id + "'>" + entity.name + "</a>",
                        ideadeveloper, unfollower, new Date());
            }
        }
    }

    /**
     * locks a specific Entity thus cant be used to relate another Entity
     *
     * @author XtarX
     * @param entityId the id of the entity we want to use to lock
     * @return "done lock" when the topic is locked
     */
    public static String lockEntity(Long entityId) {
        Entities entity = Entities.findById(entityId);
        entity.locked = true;
        entity.save();
        return "done LOCK";

    }

    /**
     * unlocks a specific Entity thus can be used to relate another Entity
     *
     * @author XtarX
     * @param entityId the id of the entity we want to use to unlock
     * @return "done UNLOCK" when the topic is locked
     */
    public static String unlockEntity(Long entityId) {
        Entities entity = Entities.findById(entityId);
        entity.locked = false;
        entity.save();
        return "done UNLOCK";

    }

    /**
     * increment the view of entity every time the page is loaded
     *
     * @author XtarX
     * @param entityId
     * @return the number of views incremented
     */
    public static int incrementViews(Long entityId) {
        Entities entity = Entities.findById(entityId);
        entity.views += 1;
        entity.save();
        return entity.views;
    }

    public String toString() {

        return this.name;
    }
}
